home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / swagg_m.zip / MEMORY.SWG / 0054_Buffers in EMS.pas < prev    next >
Pascal/Delphi Source File  |  1994-08-24  |  17KB  |  634 lines

  1. {
  2. *************** Generalized file I/O buffering *****************
  3.  
  4. The enclosed TP unit BUFFERS exports a new object BUFFERFILE. This
  5. object allows to define a variable number of buffers with a buffersize
  6. of up to $FFE0 bytes each. It exports a number of methods to tailor
  7. the behaviour of the buffer to a specific applications needs - See the
  8. following procedures for details in this area:
  9.  
  10.  - SETWRITEBIAS
  11.  - SETREADBIAS
  12.  - RESETBIAS
  13.  - ENABLEINBOUND
  14.  - ENABLEOUTBOUND
  15.  - DISABLEINBOUND
  16.  - DISABLEOUTBOUND
  17.  
  18. The buffers may be allocated in expanded memory if desired. Performance
  19. will be somewhat affected by this fact.
  20.  
  21. All methods use the same names as their counterparts in the system unit,
  22. the there should not be any problem implementing them. The only minor
  23. difference is the fact, that the READ and WRITE procedures do not accept
  24. the optional fourth parameter, which in the system unit will return the
  25. number of bytes actually read or written. This was done for performance
  26. reasons but should be very easy to change.
  27.  
  28. The unit is implemented using some of Turbo Pascals object oriented
  29. language constructs (actually my second step in this area). Some of the
  30. object oriented stuff is not really very pure code - some access to the
  31. imported data areas is direct, etc. This was done as to achieve some decent
  32. performance.
  33.  
  34. Last but not least a small example on how to use the code:
  35.  
  36. Program Test;
  37. VAR
  38.   BF : BufferFile;
  39.   L  : LongInt;
  40. begin
  41.   BF.Init(16384,5,True);
  42.   BF.SetWriteBias;           {Purely optional - may improve performance}
  43.   BF.Assign('TEST.FIL');
  44.   BF.Rewrite(4);
  45.   For L:=1 to 20000 do BF.Write(L,1);
  46.   BF.Done;
  47. end.
  48.  
  49. The code is herbey given to the public domain. If you discover any errors,
  50. I would appreciate if you would let me know.
  51.  
  52. Rolf Ernst 72311,254
  53. }
  54.  
  55. Unit Buffers;
  56.  
  57. InterFace
  58. {*********************************************************************}
  59. {****              Written 1989 by Rolf Ernst                     ****}
  60. {****                                                             ****}
  61. {****  Code requires Turbo Professional for the expanded memory   ****}
  62. {****  access. The procedures used should not take more than a    ****}
  63. {****  few lines to reproduce though.                             ****}
  64. {****                                                             ****}
  65. {****  This code is hereby in the public domain.                  ****}
  66. {*********************************************************************}
  67.  
  68. Uses Dos, TpEms;
  69.  
  70. Type
  71.   PtrRec = Record
  72.     Ofs, Seg : Word;
  73.   end;
  74.  
  75.   BigBlock = Array[0..1] Of Byte;
  76.   BigBlockPtr = ^BigBlock;
  77.   BufferPtr = ^BufferDesc;
  78.   BufferDesc = object
  79.     BufferAddr : BigBlockPtr;
  80.     EmsHandle  : Word;
  81.     InEms      : Boolean;
  82.     Size       : Word;
  83.     Next       : Pointer;
  84.     Constructor Init(BufferSize : Word; UseEms : Boolean);
  85.     Function    Map(Offset, Length : Word) : BigBlockPtr; Virtual;
  86.     Destructor  Done;
  87.   end;
  88.  
  89.   FileBufferPtr = ^FileBufferDesc;
  90.   FileBufferDesc = Object(BufferDesc)
  91.     PosBuffer   : LongInt;
  92.     BytesUsed   : Word;
  93.     Initialized : Boolean;
  94.     Modified    : Boolean;
  95.     Constructor Init(BufferSize : Word; UseEms : Boolean);
  96.   end;
  97.  
  98.   BufferChain = object
  99.     NumberOfBuffers, BlockSize:Word;
  100.     BufferHead, BufferTail : FileBufferPtr;
  101.     Procedure Init(BufSize, BufNum : Word; UseEms : Boolean);
  102.     Procedure ChainAtEnd(VAR B : FileBufferPtr);
  103.     Function  BuffersUnUsed:Word;
  104.     Procedure Done;
  105.   end;
  106.  
  107.   BufferFile=Object
  108.     F              : File;
  109.     FSize          : LongInt;
  110.     CurrentPos     : LongInt;
  111.     RecordSize     : Word;
  112.     BlockSize      : Word;
  113.     BufferS        : BufferChain;
  114.     FlushAll       : Boolean;
  115.     ReadAll        : Boolean;
  116.     NoBufferReads  : Boolean;
  117.     NoBufferWrites : Boolean;
  118.     NoBufferIng    : Boolean;
  119.  
  120.     Procedure Init(BufSize, BufNum:Word; UseEms : Boolean);
  121.               {Initialize BufNum buffers for the file, each being
  122.                Bufsize bytes big - use Expanded memory if UseEms is TRUE}
  123.  
  124.     Procedure Flush;
  125.               {Write all modified buffers to disk - does not cause DOS to
  126.                flush its buffers}
  127.  
  128.     Function  FreeBuffer : FileBufferPtr;
  129.               {Find an available Buffer - Flush a buffer if necessary}
  130.  
  131.     Procedure Read(VAR A; NumRecs : Word);
  132.               {Read a record buffered}
  133.  
  134.     Procedure DisableOutBound;
  135.               {Disable buffering when writing to a file}
  136.  
  137.     Procedure Write(VAR A; NumRecs : Word);
  138.               {Write a record buffered}
  139.  
  140.     Function  Eof:Boolean;
  141.               {Return true if the current position in the file is at its end}
  142.  
  143.     Procedure Seek(NewPos : LongInt);
  144.               {Go to a new position in the file}
  145.  
  146.     Function  FileSize:LongInt;
  147.               {Returns the size of a buffered file taking any data in the
  148.                buffers into consideration}
  149.  
  150.     Procedure Assign(Name : PathStr);
  151.               {Assign a name to a buffered file}
  152.  
  153.     Function  FilePos:LongInt;
  154.               {Returns the current position in a buffered file}
  155.  
  156.     Procedure Rewrite(RecSize : Word);
  157.               {Create a new file or overwrite an existing one}
  158.  
  159.     Procedure Reset(RecSize:Word);
  160.               {Open an existing file}
  161.  
  162.     Procedure SetWriteBias;
  163.               {Indicate, that the majority of the file operations will be
  164.                sequential writes - when a buffer needs to be flushed ALL
  165.                buffers will be flushed}
  166.  
  167.     Procedure SetReadBias;
  168.               {Indicate, that the majority of the file operations will be
  169.                sequential reads - when a buffer needs to be read ALL buffers
  170.                will be read from disk}
  171.  
  172.     Procedure ResetBias;
  173.               {Reset file access characteristics to its default values}
  174.  
  175.     Procedure DisableInBound;
  176.               {Disable buffering when reading from a dataset}
  177.  
  178.     Procedure EnableInBound;
  179.               {Enable buffering when reading from a dataset}
  180.  
  181.     Procedure EnableOutBound;
  182.               {Enable buffering when writing to a dataset}
  183.  
  184.     Procedure Done;
  185.               {Close the file and free all buffers}
  186.  
  187.   end;
  188.  
  189.  
  190. Implementation
  191.  
  192.  
  193.  
  194. Procedure EmsError;
  195. begin
  196.   Writeln('Severe Error in EMS handler');
  197.   readln;
  198.   halt;
  199. end;
  200.  
  201. Function MemToEms(BytesIn : LongInt) : Word;
  202. begin
  203.   MemToEms:=(BytesIn+16383) shr 14;
  204. end;
  205.  
  206. Procedure MapBuffer(Handle : Word; BytesInBuffer:Word);
  207. VAR
  208.   I : Word;
  209. begin
  210.   For I:=0 to Pred(MemToEms(BytesInBuffer)) do begin
  211.     If Not MapEmsPage(Handle,i,i) then EmsError;
  212.   end;
  213. end;
  214.  
  215. Procedure BufferFile.SetWriteBias;
  216. begin
  217.   FlushAll:=True;
  218.   ReadAll:=False;
  219. end;
  220.  
  221. Procedure BufferFile.DisableInBound;
  222. begin
  223.   NoBufferReads:=True;
  224. end;
  225.  
  226. Procedure BufferFile.EnableInBound;
  227. begin
  228.   NoBufferReads:=false;
  229. end;
  230.  
  231. Procedure BufferFile.DisableOutBound;
  232. begin
  233.   Flush;
  234.   NoBufferWrites:=True;
  235. end;
  236.  
  237. Procedure BufferFile.EnableOutBound;
  238. begin
  239.   NoBufferWrites:=False;
  240. end;
  241.  
  242. Procedure BufferFile.ResetBias;
  243. begin
  244.   FlushAll:=False;
  245.   ReadAll:=False;
  246.   NoBufferReads:=False;
  247.   NoBufferWrites:=False;
  248. end;
  249.  
  250. Procedure BufferFile.SetReadBias;
  251. begin
  252.   FlushAll:=False;
  253.   ReadAll:=True;
  254. end;
  255.  
  256.  
  257. Constructor BufferDesc.Init(BufferSize : Word; UseEms : Boolean);
  258. begin
  259.   InEms:=UseEms and EmsInstalled and
  260.     (EmsPagesAvail>=MemToEms(Buffersize));
  261.   Size:=BufferSize;
  262.   If InEms then begin
  263.     EmsHandle:=AllocateEMSPages(MemToEms(Size));
  264.     If EmsHandle=EmsErrorCode then EmsError;
  265.     BufferAddr:=EmsPageFramePtr;
  266.   end else GetMem(BufferAddr,Size);
  267.   Next:=Nil;
  268. end;
  269.  
  270. Function BufferDesc.Map(Offset, Length : Word) : BigBlockPtr;
  271. VAR
  272.   HighOffset : Word;
  273.   MyPointer  : BigBlockPTr;
  274. begin
  275.   MyPointer:=BufferAddr;
  276.   Inc(PtrRec(MyPointer).Ofs,Offset);
  277.   Map:=MyPointer;
  278.   If InEms then begin
  279.     HighOffset:=Pred(Offset+Length);
  280.     Offset:=Offset Shr 14;
  281.     HighOffset:=HighOffset shr 14;
  282.     repeat
  283.       If Not MapEmsPage(EMSHandle,Offset,Offset) then EmsError;
  284.       INC(Offset);
  285.     until Offset>HighOffset;
  286.   end;
  287. end;
  288.  
  289. Destructor BufferDesc.Done;
  290. begin
  291.   IF InEms then begin
  292.     If Not DeallocateEmsHandle(Emshandle) then EmsError;
  293.   end else FreeMem(BufferAddr,Size);
  294. end;
  295.  
  296. Constructor FileBufferDesc.Init(BufferSize : Word; UseEms : Boolean);
  297. begin
  298.   BufferDesc.Init(BufferSize, UseEms);
  299.   Initialized:=False;
  300.   Modified:=False;
  301. end;
  302.  
  303. Procedure BufferChain.Init(BufSize, BufNum : Word; UseEms : Boolean);
  304. VAR
  305.   I : Word;
  306. begin
  307.   NumberOfBuffers:=BufNum;
  308.   BufferTail:=Nil;
  309.   For i:=1 to BufNum do begin
  310.     New(BufferHead,Init(BufSize,UseEms));
  311.     BufferHead^.Next:=BufferTail;
  312.     BufferTail:=BufferHead;
  313.   end;
  314.   While BufferTail^.Next<>Nil do BufferTail:=BufferTail^.Next;
  315. end;
  316.  
  317. Procedure BufferChain.ChainAtEnd(VAR B : FileBufferPtr);
  318. VAR
  319.   BufPtr:FileBufferPtr;
  320. begin
  321.   If (NumberOfBuffers>1) and (B<>BufferTail) then begin
  322.     BufferTail^.Next:=B;
  323.     BufferTail:=B;
  324.     If B=BufferHead then begin
  325.       BufferHead:=B^.Next;
  326.       B^.Next:=Nil;
  327.     end else begin
  328.       Bufptr:=BufferHead;
  329.       While BufPtr^.Next<>B do Bufptr:=BufPtr^.Next;
  330.       BufPtr^.Next:=B^.Next;
  331.       B^.Next:=Nil;
  332.     end;
  333.   end;
  334. end;
  335.  
  336.  
  337. Procedure BufferFile.Init(BufSize, BufNum:Word; UseEms : Boolean);
  338. VAR
  339.   I : Word;
  340. begin
  341.   If (BufSize=0) or (BufNum=0) then begin
  342.     NoBufferIng:=True;
  343.     exit;
  344.   end;
  345.   UseEms:=UseEms and EmsInstalled and
  346.     (EmsPagesAvail>=BufNum * MemToEms(Bufsize));
  347.   Buffers.Init(BufSize, BufNum, USeEms);
  348.   FlushAll:=False;
  349.   ReadAll:=False;
  350.   NoBufferReads:=False;
  351.   NoBufferWrites:=False;
  352.   NoBuffering:=False;
  353.   BlockSize:=BufSize;
  354. end;
  355.  
  356. Function BufferFile.FreeBuffer:FileBufferPtr;
  357. VAR
  358.   BufPtr,SavePtr : FileBufferPtr;
  359.   LowPos : LongInt;
  360.   MyPointer : Pointer;
  361. begin
  362.   BufPtr:=Buffers.BufferHead;
  363.   LowPos:=$7fffffff;
  364.   While BufPtr<>Nil do begin
  365.     With BufPtr^ do begin
  366.       If (Not Modified) or (Not initialized) then begin
  367.         FreeBuffer:=BufPtr;
  368.         Modified:=False;
  369.         FreeBuffer:=BufPtr;
  370.         Buffers.ChainAtEnd(BufPtr);
  371.         Exit;
  372.       end;
  373.       If PosBuffer<LowPos then begin
  374.         LowPos:=PosBuffer;
  375.         SavePtr:=BufPtr;
  376.       end;
  377.       BufPtr:=Next;
  378.     end;
  379.   end;
  380.   If FlushAll then begin
  381.     Flush;
  382.     FreeBuffer:=Buffers.BufferHead;
  383.   end;
  384.   With SavePtr^ do begin
  385.     System.Seek(F,PosBuffer);
  386.     MyPointer:=Map(0,BytesUsed);
  387.     BlockWrite(F,MyPointer^,BytesUsed);
  388.     BytesUsed:=0;
  389.     Modified:=False;
  390.   end;
  391.   FreeBuffer:=SavePtr;
  392.   Buffers.ChainAtEnd(SavePtr);
  393. end;
  394.  
  395. Procedure BufferFile.Flush;
  396. VAR
  397.   BufPtr : FileBufferPtr;
  398.   MyPointer : Pointer;
  399. begin
  400.   If NoBuffering then exit;
  401.   BufPtr:=Buffers.BufferHead;
  402.   While BufPtr<>Nil do begin
  403.     With BufPTr^ do begin
  404.       If Modified then begin
  405.         System.Seek(F,PosBuffer);
  406.         MyPointer:=Map(0,BytesUsed);
  407.         BlockWrite(F,BufferAddr^,BytesUsed);
  408.         Modified:=False;
  409.       end;
  410.       BufPtr:=Next;
  411.     end;
  412.   end;
  413. end;
  414.  
  415. Function  BufferCHain.BuffersUnUsed:Word;
  416. VAR
  417.   BufPtr : FileBufferPtr;
  418.   Count : Word;
  419. begin
  420.   Count:=0;
  421.   BufPtr:=BufferHead;
  422.   While BufPtr<>Nil do begin
  423.     With BufPtr^ do begin
  424.       If (Not Initialized) or (Not Modified) then Inc(Count);
  425.       BufPtr:=Next;
  426.     end;
  427.   end;
  428.   BuffersUnUsed:=Count;
  429. end;
  430.  
  431. Function BufferFile.FileSize:LongInt;
  432. begin
  433.   If NoBuffering then FileSize:=System.FIleSize(F) else
  434.     FileSize:=Fsize div RecordSize;
  435. end;
  436.  
  437. Function BufferFile.FilePos:LongInt;
  438. begin
  439.   If NoBuffering then FilePos:=System.FilePos(F) else
  440.     FilePos:=CurrentPos div RecordSize;
  441. end;
  442.  
  443. Procedure BufferFile.Read(VAR A; NumRecs : Word);
  444. VAR
  445.   I,J    : Word;
  446.   BufPtr   :  FileBufferPtr;
  447.   TargetPtr : BigBlockPtr;
  448.   More  : Boolean;
  449.   BaseBufferToGet : LongInt;
  450.   MyPointer : Pointer;
  451. begin
  452.   If NoBuffering then BlockRead(F,A,NuMRecs) else begin
  453.     NumRecs:=NumRecs*RecordSize;
  454.     TargetPtr:=@A;
  455.     Repeat
  456.       BaseBufferToGet:=CurrentPos-(CurrentPos Mod BlockSize);
  457.       BufPtr:=Buffers.BufferHead;
  458.       More:=True;
  459.       While (BufPtr<>Nil) and More Do begin
  460.         With BufPtr^ do begin
  461.           If (PosBuffer=BaseBufferToGet) and Initialized then more:=False else
  462.           BufPtr:=Next;
  463.         end;
  464.       end;
  465.       If BufPtr=Nil then begin
  466.         If NoBufferReads then begin
  467.           System.Seek(F,CurrentPos);
  468.           BlockRead(F,TargetPtr^,NumRecs);
  469.           Inc(CurrentPos,NumRecs);
  470.           exit;
  471.         end;
  472.         BufPtr:=FreeBuffer;
  473.         With BufPtr^ do begin
  474.           System.Seek(F,BaseBufferToGet);
  475.           PosBuffer:=BaseBufferToGet;
  476.           MyPointer:=Map(0,BlockSize);
  477.           BlockRead(F,MyPointer^,BlockSize,BytesUsed);
  478.           Initialized:=True;
  479.         end;
  480.         If ReadAll then begin
  481.           J:=Buffers.BuffersUnUsed;
  482.           If J>0 then Dec(j);
  483.           I:=1;
  484.           While (I<= J) and (BufPtr^.BytesUsed=BlockSize) do begin
  485.             Inc(BaseBufferToGet,BlockSize);
  486.             BufPtr:=FreeBuffer;
  487.             With BufPtr^ do begin
  488.               PosBuffer:=BaseBufferToGet;
  489.               MyPointer:=Map(0,BlockSize);
  490.               BlockRead(F,MyPointer^,BlockSize,BytesUsed);
  491.               Initialized:=True;
  492.             end;
  493.             Inc(I);
  494.           end;
  495.         end;
  496.       end else begin
  497.         With BufPtr^ do begin
  498.           J:=CurrentPos-PosBuffer;
  499.           I:=BytesUsed-j;
  500.           If I>NumRecs then I:=NumRecs;
  501.           MyPointer:=Map(J,I);
  502.           Move(MyPointer^,TargetPtr^,I);
  503.           Inc(CurrentPos,I);
  504.           Dec(NumRecs,I);
  505.           Inc(PtrRec(TargetPtr).Ofs,I);
  506.         end;
  507.       end;
  508.     until NumRecs=0;
  509.   end;
  510. end;
  511.  
  512. Procedure BufferFile.Write(VAR A; NumRecs : Word);
  513. VAR
  514.   I,J : WOrd;
  515.   BufPtr : FileBufferPtr;
  516.   TargetPTr,MyPointer : Pointer;
  517.   BaseBufferToGet : LongInt;
  518.   BytesNeeded : LongInt;
  519.   OK,More : Boolean;
  520. begin
  521.   If NoBuffering then BlockWrite(F,A,NumRecs) else begin
  522.     TargetPtr:=@A;
  523.     NumRecs:=NumRecs*RecordSize;
  524.     Repeat
  525.       BaseBufferToGet:=CUrrentPos-(CurrentPos Mod BlockSize);
  526.       BufPtr:=Buffers.BufferHead;
  527.       More:=True;
  528.       While (BufPtr<>Nil) and More Do begin
  529.         With BufPtr^ do begin
  530.           If (Initialized) and (BaseBufferToGet=PosBuffer) then begin
  531.             BytesNeeded:=CurrentPos-PosBuffer+NumRecs;
  532.             If BytesNeeded>BytesUsed then begin
  533.               If BytesNeeded>BlockSize then BytesUsed:=BlockSize else
  534.               BytesUsed:=BytesNeeded;
  535.               Fsize:=BaseBufferToGet+BytesUsed;
  536.             end;
  537.             More:=False;
  538.           end else BufPtr:=Next;
  539.         end;
  540.       end;
  541.       If BufPtr=Nil then begin
  542.         If NoBufferWrites then begin
  543.           If BaseBufferToGet<>CurrentPos then begin
  544.             System.Seek(F,CurrentPos);
  545.             BlockWrite(F,A,NumRecs);
  546.             Inc(CurrentPos,NumRecs);
  547.             exit;
  548.           end;
  549.         end;
  550.         BufPtr:=FreeBuffer;
  551.         With BufPtr^ do begin
  552.           System.Seek(F,BaseBufferToGet);
  553.           PosBuffer:=BaseBufferToGet;
  554.           If PosBuffer<SyStem.FileSize(F) then begin
  555.             MyPointer:=Map(0,BlockSize);
  556.             BlockRead(F,MyPointer^,BlockSize,BytesUsed);
  557.           end else BytesUsed:=0;
  558.           Initialized:=True;
  559.         end;
  560.       end else begin
  561.         With BufPtr^ do begin
  562.           J:=CurrentPos-PosBuffer;
  563.           I:=BytesUsed-j;
  564.           If I>NumRecs then I:=NumRecs;
  565.           MyPointer:=Map(J,I);
  566.           Move(TargetPtr^,MyPointer^,I);
  567.           Modified:=True;
  568.           Inc(CurrentPos,I);
  569.           Dec(NumRecs,I);
  570.           Inc(PtrRec(TargetPtr).Ofs,I);
  571.         end;
  572.       end;
  573.     until NumRecs=0;
  574.   end;
  575. end;
  576.  
  577. Function BufferFile.Eof:Boolean;
  578. begin
  579.   If NoBuffering then Eof:=System.Eof(F) else
  580.     Eof:=CurrentPos=Fsize;
  581. end;
  582.  
  583. Procedure BufferFile.Seek(NewPos : LongInt);
  584. begin
  585.   If NoBuffering then System.Seek(F,Newpos) else
  586.     CurrentPos:=NewPos*RecordSize;
  587. end;
  588.  
  589. Procedure BufferFile.Assign(Name : PathStr);
  590. begin
  591.   System.Assign(F,Name);
  592. end;
  593.  
  594. Procedure BufferFile.Rewrite(RecSize:Word);
  595. begin
  596.   RecordSize:=RecSize;
  597.   If Not NoBuffering then Recsize:=1;
  598.   System.Rewrite(F,RecSize);
  599.   Fsize:=0;
  600.   CurrentPos:=0;
  601. end;
  602.  
  603. Procedure BufferFile.Reset(RecSize : Word);
  604. begin
  605.   RecordSize:=RecSize;
  606.   If Not NoBuffering then RecSize:=1;
  607.   System.Reset(F,RecSize);
  608.   Fsize:=System.FileSize(F);
  609.   CurrentPos:=0;
  610. end;
  611.  
  612. Procedure BufferChain.Done;
  613. begin
  614.   repeat
  615.     with BufferHead^ do begin
  616.       BufferTail:=BufferHead^.Next;
  617.       Dispose(BufferHead,Done);
  618.       BufferHead:=BufferTail;
  619.     end;
  620.   until Bufferhead=Nil;
  621. end;
  622.  
  623. Procedure BufferFile.Done;
  624. VAR
  625.   BufferTail : BufferPtr;
  626.   Ok : Boolean;
  627. begin
  628.   Flush;
  629.   Close(F);
  630.   If Not NoBuffering then Buffers.Done;
  631. end;
  632. end.
  633.  
  634.